/*-------------------------------------------------------------------------
 *
 * numutils.c
 *	  内置数字类型的输入/输出实用函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/numutils.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>
#include <limits.h>
#include <ctype.h>

#include "common/int.h"
#include "utils/builtins.h"
#include "port/pg_bitutils.h"

/*
 * 所有两位数的表。这用于通过将数字对复制到最终输出中加速十进制数字生成。
 */
static const char DIGIT_TABLE[200] =
"00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
"10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
"20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
"30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
"40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
"50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
"60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
"70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
"80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
"90" "91" "92" "93" "94" "95" "96" "97" "98" "99";

/*
 * 改编自 http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
 */
static inline int fc_decimalLength32(const uint32 fc_v)
{
	int			fc_t;
	static const uint32 fc_PowersOfTen[] = {
		1, 10, 100,
		1000, 10000, 100000,
		1000000, 10000000, 100000000,
		1000000000
	};

	/*
	 * 通过将以2为底的对数除以10的以2为底的对数的一个足够好的近似值来计算以10为底的对数
	 */
	fc_t = (pg_leftmost_one_pos32(fc_v) + 1) * 1233 / 4096;
	return fc_t + (fc_v >= fc_PowersOfTen[fc_t]);
}

static inline int fc_decimalLength64(const uint64 fc_v)
{
	int			fc_t;
	static const uint64 fc_PowersOfTen[] = {
		UINT64CONST(1), UINT64CONST(10),
		UINT64CONST(100), UINT64CONST(1000),
		UINT64CONST(10000), UINT64CONST(100000),
		UINT64CONST(1000000), UINT64CONST(10000000),
		UINT64CONST(100000000), UINT64CONST(1000000000),
		UINT64CONST(10000000000), UINT64CONST(100000000000),
		UINT64CONST(1000000000000), UINT64CONST(10000000000000),
		UINT64CONST(100000000000000), UINT64CONST(1000000000000000),
		UINT64CONST(10000000000000000), UINT64CONST(100000000000000000),
		UINT64CONST(1000000000000000000), UINT64CONST(10000000000000000000)
	};

	/*
	 * 通过将以2为底的对数除以10的以2为底的对数的一个足够好的近似值来计算以10为底的对数
	 */
	fc_t = (pg_leftmost_one_pos64(fc_v) + 1) * 1233 / 4096;
	return fc_t + (fc_v >= fc_PowersOfTen[fc_t]);
}

/*
 * 将输入字符串转换为有符号的16位整数。
 *
 * 允许任何数量的前导或尾随空格字符。输入格式错误或溢出时将抛出 ereport()。
 *
 * 注意：将输入累加为负数，以处理最小负数的二进制补码表示，最小负数无法作为正数表示。
 */
int16 pg_strtoint16(const char *fc_s)
{
	const char *fc_ptr = fc_s;
	int16		fc_tmp = 0;
	bool		fc_neg = false;

	/*
							 * 已经有了月份和小时？那么假设
							 * 是分钟
							 */
	while (likely(*fc_ptr) && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	/* 处理符号 */
	if (*fc_ptr == '-')
	{
		fc_ptr++;
		fc_neg = true;
	}
	else if (*fc_ptr == '+')
		fc_ptr++;

	/* 至少需要一个数字 */
	if (unlikely(!isdigit((unsigned char) *fc_ptr)))
		goto invalid_syntax;

	/* 处理数字 */
	while (*fc_ptr && isdigit((unsigned char) *fc_ptr))
	{
		int8		fc_digit = (*fc_ptr++ - '0');

		if (unlikely(pg_mul_s16_overflow(fc_tmp, 10, &fc_tmp)) ||
			unlikely(pg_sub_s16_overflow(fc_tmp, fc_digit, &fc_tmp)))
			goto out_of_range;
	}

	/* 允许尾随空格，但不允许其他尾随字符 */
	while (*fc_ptr != '\0' && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	if (unlikely(*fc_ptr != '\0'))
		goto invalid_syntax;

	if (!fc_neg)
	{
		/* 如果输入是最小负数，则可能失败 */
		if (unlikely(fc_tmp == PG_INT16_MIN))
			goto out_of_range;
		fc_tmp = -fc_tmp;
	}

	return fc_tmp;

out_of_range:
	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("value \"%s\" is out of range for type %s",
					fc_s, "smallint")));

invalid_syntax:
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
			 errmsg("invalid input syntax for type %s: \"%s\"",
					"smallint", fc_s)));

	return 0;					/* 保持编译器安静 */
}

/*
 * 将输入字符串转换为有符号的32位整数。
 *
 * 允许任何数量的前导或尾随空格字符。输入格式错误或溢出时将抛出 ereport()。
 *
 * 注意：将输入累加为负数，以处理最小负数的二进制补码表示，最小负数无法作为正数表示。
 */
int32 pg_strtoint32(const char *fc_s)
{
	const char *fc_ptr = fc_s;
	int32		fc_tmp = 0;
	bool		fc_neg = false;

	/*
							 * 已经有了月份和小时？那么假设
							 * 是分钟
							 */
	while (likely(*fc_ptr) && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	/* 处理符号 */
	if (*fc_ptr == '-')
	{
		fc_ptr++;
		fc_neg = true;
	}
	else if (*fc_ptr == '+')
		fc_ptr++;

	/* 至少需要一个数字 */
	if (unlikely(!isdigit((unsigned char) *fc_ptr)))
		goto invalid_syntax;

	/* 处理数字 */
	while (*fc_ptr && isdigit((unsigned char) *fc_ptr))
	{
		int8		fc_digit = (*fc_ptr++ - '0');

		if (unlikely(pg_mul_s32_overflow(fc_tmp, 10, &fc_tmp)) ||
			unlikely(pg_sub_s32_overflow(fc_tmp, fc_digit, &fc_tmp)))
			goto out_of_range;
	}

	/* 允许尾随空格，但不允许其他尾随字符 */
	while (*fc_ptr != '\0' && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	if (unlikely(*fc_ptr != '\0'))
		goto invalid_syntax;

	if (!fc_neg)
	{
		/* 如果输入是最小负数，则可能失败 */
		if (unlikely(fc_tmp == PG_INT32_MIN))
			goto out_of_range;
		fc_tmp = -fc_tmp;
	}

	return fc_tmp;

out_of_range:
	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("value \"%s\" is out of range for type %s",
					fc_s, "integer")));

invalid_syntax:
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
			 errmsg("invalid input syntax for type %s: \"%s\"",
					"integer", fc_s)));

	return 0;					/* 保持编译器安静 */
}

/*
 * 将输入字符串转换为有符号的64位整数。
 *
 * 允许任何数量的前导或尾随空格字符。输入格式错误或溢出时将抛出 ereport()。
 *
 * 注意：将输入累加为负数，以处理最小负数的二进制补码表示，最小负数无法作为正数表示。
 */
int64 pg_strtoint64(const char *fc_s)
{
	const char *fc_ptr = fc_s;
	int64		fc_tmp = 0;
	bool		fc_neg = false;

	/*
	 * 自行扫描，而不是依赖可能存在问题的 sscanf
	 * 对于长整型。
	 *
	 * 由于 INT64_MIN 不能作为正的64位整数存储，
	 * 因此将值累积为负数。
	 */

	/*
							 * 已经有了月份和小时？那么假设
							 * 是分钟
							 */
	while (*fc_ptr && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	/* 处理符号 */
	if (*fc_ptr == '-')
	{
		fc_ptr++;
		fc_neg = true;
	}
	else if (*fc_ptr == '+')
		fc_ptr++;

	/* 至少需要一个数字 */
	if (unlikely(!isdigit((unsigned char) *fc_ptr)))
		goto invalid_syntax;

	/* 处理数字 */
	while (*fc_ptr && isdigit((unsigned char) *fc_ptr))
	{
		int8		fc_digit = (*fc_ptr++ - '0');

		if (unlikely(pg_mul_s64_overflow(fc_tmp, 10, &fc_tmp)) ||
			unlikely(pg_sub_s64_overflow(fc_tmp, fc_digit, &fc_tmp)))
			goto out_of_range;
	}

	/* 允许尾随空格，但不允许其他尾随字符 */
	while (*fc_ptr != '\0' && isspace((unsigned char) *fc_ptr))
		fc_ptr++;

	if (unlikely(*fc_ptr != '\0'))
		goto invalid_syntax;

	if (!fc_neg)
	{
		/* 如果输入是最小负数，则可能失败 */
		if (unlikely(fc_tmp == PG_INT64_MIN))
			goto out_of_range;
		fc_tmp = -fc_tmp;
	}

	return fc_tmp;

out_of_range:
	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("value \"%s\" is out of range for type %s",
					fc_s, "bigint")));

invalid_syntax:
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
			 errmsg("invalid input syntax for type %s: \"%s\"",
					"bigint", fc_s)));

	return 0;					/* 保持编译器安静 */
}

/*
 * pg_itoa：将有符号的16位整数转换为其字符串表示并返回 strlen(a)。
 *
 * 调用者必须确保 'a' 指向足够的内存来保存结果（至少7个字节，包括前导符号和尾随 NUL）。
 *
 * 似乎没有必要单独实现这一点。
 */
int pg_itoa(int16 fc_i, char *fc_a)
{
	return pg_ltoa((int32) fc_i, fc_a);
}

/*
 * pg_ultoa_n: 将无符号 32 位整数转换为其字符串表示形式，
 * 不是以 NUL 结尾，并返回该字符串表示的长度
 *
 * 调用者必须确保 'a' 指向足够的内存来容纳结果（至少 10 字节）
 */
int pg_ultoa_n(uint32 fc_value, char *fc_a)
{
	int			fc_olength,
				fc_i = 0;

	/* 退化情况 */
	if (fc_value == 0)
	{
		*fc_a = '0';
		return 1;
	}

	fc_olength = fc_decimalLength32(fc_value);

	/* 计算结果字符串。 */
	while (fc_value >= 10000)
	{
		const uint32 fc_c = fc_value - 10000 * (fc_value / 10000);
		const uint32 fc_c0 = (fc_c % 100) << 1;
		const uint32 fc_c1 = (fc_c / 100) << 1;

		char	   *fc_pos = fc_a + fc_olength - fc_i;

		fc_value /= 10000;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c0, 2);
		memcpy(fc_pos - 4, DIGIT_TABLE + fc_c1, 2);
		fc_i += 4;
	}
	if (fc_value >= 100)
	{
		const uint32 fc_c = (fc_value % 100) << 1;

		char	   *fc_pos = fc_a + fc_olength - fc_i;

		fc_value /= 100;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c, 2);
		fc_i += 2;
	}
	if (fc_value >= 10)
	{
		const uint32 fc_c = fc_value << 1;

		char	   *fc_pos = fc_a + fc_olength - fc_i;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c, 2);
	}
	else
	{
		*fc_a = (char) ('0' + fc_value);
	}

	return fc_olength;
}

/*
 * pg_ltoa: 将有符号 32 位整数转换为其字符串表示形式并
 * 返回 strlen(a)。
 *
 * 确保 a 至少为 12 字节长是调用者的责任，
 * 这足以容纳一个负号、一个最大长度的 int32，以及上述终止的 NUL。
 */
int pg_ltoa(int32 fc_value, char *fc_a)
{
	uint32		fc_uvalue = (uint32) fc_value;
	int			fc_len = 0;

	if (fc_value < 0)
	{
		fc_uvalue = (uint32) 0 - fc_uvalue;
		fc_a[fc_len++] = '-';
	}
	fc_len += pg_ultoa_n(fc_uvalue, fc_a + fc_len);
	fc_a[fc_len] = '\0';
	return fc_len;
}

/*
 * 获取十进制表示，不以 NUL 结尾，并返回其长度。
 * 调用者必须确保 a 的指向至少为 MAXINT8LEN 字节。
 */
int pg_ulltoa_n(uint64 fc_value, char *fc_a)
{
	int			fc_olength,
				fc_i = 0;
	uint32		fc_value2;

	/* 退化情况 */
	if (fc_value == 0)
	{
		*fc_a = '0';
		return 1;
	}

	fc_olength = fc_decimalLength64(fc_value);

	/* 计算结果字符串。 */
	while (fc_value >= 100000000)
	{
		const uint64 fc_q = fc_value / 100000000;
		uint32		fc_value2 = (uint32) (fc_value - 100000000 * fc_q);

		const uint32 fc_c = fc_value2 % 10000;
		const uint32 fc_d = fc_value2 / 10000;
		const uint32 fc_c0 = (fc_c % 100) << 1;
		const uint32 fc_c1 = (fc_c / 100) << 1;
		const uint32 fc_d0 = (fc_d % 100) << 1;
		const uint32 fc_d1 = (fc_d / 100) << 1;

		char	   *fc_pos = fc_a + fc_olength - fc_i;

		fc_value = fc_q;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c0, 2);
		memcpy(fc_pos - 4, DIGIT_TABLE + fc_c1, 2);
		memcpy(fc_pos - 6, DIGIT_TABLE + fc_d0, 2);
		memcpy(fc_pos - 8, DIGIT_TABLE + fc_d1, 2);
		fc_i += 8;
	}

	/* 切换到 32 位以提高速度 */
	fc_value2 = (uint32) fc_value;

	if (fc_value2 >= 10000)
	{
		const uint32 fc_c = fc_value2 - 10000 * (fc_value2 / 10000);
		const uint32 fc_c0 = (fc_c % 100) << 1;
		const uint32 fc_c1 = (fc_c / 100) << 1;

		char	   *fc_pos = fc_a + fc_olength - fc_i;

		fc_value2 /= 10000;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c0, 2);
		memcpy(fc_pos - 4, DIGIT_TABLE + fc_c1, 2);
		fc_i += 4;
	}
	if (fc_value2 >= 100)
	{
		const uint32 fc_c = (fc_value2 % 100) << 1;
		char	   *fc_pos = fc_a + fc_olength - fc_i;

		fc_value2 /= 100;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c, 2);
		fc_i += 2;
	}
	if (fc_value2 >= 10)
	{
		const uint32 fc_c = fc_value2 << 1;
		char	   *fc_pos = fc_a + fc_olength - fc_i;

		memcpy(fc_pos - 2, DIGIT_TABLE + fc_c, 2);
	}
	else
		*fc_a = (char) ('0' + fc_value2);

	return fc_olength;
}

/*
 * pg_lltoa: 将有符号 64 位整数转换为其字符串表示形式并
 * 返回 strlen(a)。
 *
 * 调用者必须确保 'a' 指向足够的内存来容纳结果
 * （至少 MAXINT8LEN + 1 字节，包括前导符号和尾随的 NUL）。
 */
int pg_lltoa(int64 fc_value, char *fc_a)
{
	uint64		fc_uvalue = fc_value;
	int			fc_len = 0;

	if (fc_value < 0)
	{
		fc_uvalue = (uint64) 0 - fc_uvalue;
		fc_a[fc_len++] = '-';
	}

	fc_len += pg_ulltoa_n(fc_uvalue, fc_a + fc_len);
	fc_a[fc_len] = '\0';
	return fc_len;
}


/*
 * pg_ultostr_zeropad
 *		将 'value' 转换为存储在 'str' 中的十进制字符串表示。
 *		'minwidth' 指定结果的最小宽度；任何额外的空间
 *		通过在数字前加零来填充。
 *
 * 返回字符串结果的结束地址（最后一个写入的字符
 * 加 1）。注意不写入 NUL 终止符。
 *
 * 该函数的预期用例是构建包含多个单个数字的字符串，例如：
 *
 *	str = pg_ultostr_zeropad(str, hours, 2);
 *	*str++ = ':';
 *	str = pg_ultostr_zeropad(str, mins, 2);
 *	*str++ = ':';
 *	str = pg_ultostr_zeropad(str, secs, 2);
 *	*str = '\0';
 *
 * 注意：调用者必须确保 'str' 指向足够的内存来容纳结果。
 */
char * pg_ultostr_zeropad(char *fc_str, uint32 fc_value, int32 fc_minwidth)
{
	int			fc_len;

	Assert(fc_minwidth > 0);

	if (fc_value < 100 && fc_minwidth == 2)	/* 常见情况的快捷方式 */
	{
		memcpy(fc_str, DIGIT_TABLE + fc_value * 2, 2);
		return fc_str + 2;
	}

	fc_len = pg_ultoa_n(fc_value, fc_str);
	if (fc_len >= fc_minwidth)
		return fc_str + fc_len;

	memmove(fc_str + fc_minwidth - fc_len, fc_str, fc_len);
	memset(fc_str, '0', fc_minwidth - fc_len);
	return fc_str + fc_minwidth;
}

/*
 * pg_ultostr
 *		将 'value' 转换为存储在 'str' 中的十进制字符串表示。
 *
 * 返回字符串结果的结束地址（最后一个写入的字符
 * 加 1）。注意不写入 NUL 终止符。
 *
 * 该函数的预期用例是构建包含多个单个数字的字符串，例如：
 *
 *	str = pg_ultostr(str, a);
 *	*str++ = ' ';
 *	str = pg_ultostr(str, b);
 *	*str = '\0';
 *
 * 注意：调用者必须确保 'str' 指向足够的内存来容纳结果。
 */
char * pg_ultostr(char *fc_str, uint32 fc_value)
{
	int			fc_len = pg_ultoa_n(fc_value, fc_str);

	return fc_str + fc_len;
}
